;; Copyright (c) [2022] [bobwxc]
;; This file is licensed under Mulan PubL v2.
;; You can use this software according to the terms and conditions of the Mulan PubL v2.
;; You may obtain a copy of Mulan PubL v2 at:
;;          http://license.coscl.org.cn/MulanPubL-2.0
;; THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
;; EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
;; MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
;; See the Mulan PubL v2 for more details.

(defpackage #:snowflake-id
  (:nicknames #:snid)
  (:use #:CL
        #:ctime)
  (:export #:genids
           #:*genids-counter*
           #:genids2
           #:*genids2-counter*
           #:genidm
           #:*genidm-counter*
           #:genid))

(in-package #:snowflake-id)

(defconstant +snid-epoch+ 1640966400
  "2022-01-01T00:00:00+0800")

(defun incf-counter (counter &optional (maxi 4096))
  (if (< counter maxi)
      (incf counter)
    (setf counter 0)))

(defparameter *genids-counter* 0)

(defun genids (nodeid &optional counter)
  ;; 64 bits
  ;; |- 0 zero
  ;; |- 41 timestamp (second)
  ;; |- 10 node id
  ;; \_ 12 counter for the same time

  ;; max 4096 id per second per node
  (when (not counter)
    (setf *genids-counter* (incf-counter *genids-counter*))
    (setf counter *genids-counter*))
  (let ((r (+ (ash (mod (- (get-universal-time) +snid-epoch+) 2199023255552) 22) ;; 2^41
              (ash (mod nodeid 1024) 12)
              (mod counter 4096))))
    (declare ((unsigned-byte 64) r))
    r))

(defparameter *genids2-counter* 0)

(defun genids2 (nodeid &optional counter)
  ;; 64 bits
  ;; |- 0 zero
  ;; |- 31 timestamp (second)
  ;; |- 10 node id
  ;; \_ 22 counter for the same time

  ;; max 4194304 id per second per node
  (when (not counter)
    (setf *genids2-counter* (incf-counter *genids2-counter* 4194304))
    (setf counter *genids2-counter*))
  (let ((r (+ (ash (mod (- (get-universal-time) +snid-epoch+) 2147483648) 32) ;; 2^31
              ;; TODO: change zero year
              (ash (mod nodeid 1024) 22)
              (mod counter 4194304)))) ;; 2^22 = 4194304
    (declare ((unsigned-byte 64) r))
    r))

(defparameter *genidm-counter* 0)

(defun genidm (nodeid &optional counter )
  ;; 64 bits
  ;; |- 0 zero
  ;; |- 41 timestamp (ms)
  ;; |- 10 node id
  ;; \_ 12 counter for the same time

  ;; max 4096 id per second per node
  (when (not counter)
    (setf *genidm-counter* (incf-counter *genidm-counter*))
    (setf counter *genidm-counter*))
  (let* ((us (ctime:c-gettimeofday))
         (ms (+ (* (- (car us) +snid-epoch+) 1000)
                (floor (/ (cdr us) 1000))))
         (r (+ (ash (mod ms 2199023255552) 22) ;; 2^41
               (ash (mod nodeid 1024) 12)
               (mod counter 4096))))
    (declare ((unsigned-byte 64) r))
    r))

(defun genid (nodeid &optional counter)
  (genidm nodeid counter))

